/*******************************************************************\
| Vector3.h
|
|--------------------------------------------------------------------
| Copyright 2005. Martin Fleisz.
| All rights reserved.
|
|--------------------------------------------------------------------
| CREATED:		2006/2/21
| AUTHOR:		Martin Fleisz
|
|--------------------------------------------------------------------
| DESCRIPTION:
| 
| 3 component vector class
\********************************************************************/
#ifndef VECTOR3_H
#define VECTOR3_H

#include "MathBase.h"

namespace Math
{

//*****************************************************************************
// class overview:
// @short:
//  3 component vector class
// @detailed:
//  Vector class for vector calculations in three dimensional space.
//-----------------------------------------------------------------------------
class Vector3
{
	public:		
		// the vector's components
		union
		{
			struct
			{
				float m_X;
				float m_Y;
				float m_Z;
			};
            float m_Xyz[3];
		};
		
	public:
		// constructors
		inline Vector3();
		inline Vector3(const Vector3& p_Vec);
		explicit inline Vector3(const float* p_Xyz);
		inline Vector3(const float p_X, const float p_Y, const float p_Z);

	public:
		// +/- operator (vector sign)
		inline Vector3	operator+() const;
		inline Vector3	operator-() const;

		// +=, -=, *=, /= operators
		inline Vector3&	operator+=(const Vector3& p_Vec);
		inline Vector3&	operator-=(const Vector3& p_Vec);
		inline float	operator*=(const Vector3& p_Vec);
		inline Vector3&	operator*=(const float p_Value);
		inline Vector3&	operator/=(const float p_Value);

		// vector addition, substraction and dot product
		inline friend const Vector3	operator+(const Vector3& p_Vec1, const Vector3& p_Vec2);
		inline friend const Vector3	operator-(const Vector3& p_Vec1, const Vector3& p_Vec2);
		inline friend float			operator*(const Vector3& p_Vec1, const Vector3& p_Vec2);

		// scalar multiplication
		inline friend const Vector3	operator*(const Vector3& p_Vec, const float p_Value);
		inline friend const Vector3	operator*(const float p_Value, const Vector3& p_Vec);

		// scalar division
		inline friend const Vector3	operator/(const Vector3& p_Vec, const float p_Value);
		inline friend const Vector3	operator/(const float p_Value, const Vector3& p_Vec);

		// assignment operator
		inline Vector3&	operator=(const Vector3& p_Vec);

		// compare operators
		inline friend bool	operator==(const Vector3& p_Vec1, const Vector3& p_Vec2);
		inline friend bool	operator<(const Vector3& p_Vec1, const Vector3& p_Vec2);

	public:

		// set the vector's components
		inline void		Set(const float* p_Xyz);
		inline void		Set(const Vector3& p_Vec);
		inline void		Set(const float p_X, const float p_Y, const float p_Z);

		// zeros the vector
		inline void		Zero();

		// return length of the vector (or square of the length for LengthSqr)
		inline float	Length()	const;
        inline float	LengthSqr()	const;        

		// normalize vector (the Fast function uses the reciprocal sqrt function
		// but is not as accurate as the standard Normalize function)
		inline void		Normalize();
		inline void		NormalizeFast();

		// cross product
		inline Vector3&				Cross(const Vector3& p_Vec);
		inline friend const Vector3	Cross(const Vector3& p_Vec1, const Vector3& p_Vec2);

		// interpolates p_Vec1 and p_Vec2 and stores the result in the current vector
		inline void		Interpolate(const Vector3& p_Vec1, const Vector3& p_Vec2, const float p_Value);

	public:
		// special predefined values
		static const Vector3 ZERO;
		static const Vector3 INFINITY;
		static const Vector3 UNIT_X;
		static const Vector3 UNIT_Y;
		static const Vector3 UNIT_Z;
};

/* ***************************************************************** *\
|  INLINE FUNCTIONS
\* ***************************************************************** */
inline Vector3::Vector3() 
{ 
}

inline Vector3::Vector3(const float p_X, const float p_Y, const float p_Z) 
{ 
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
}

inline Vector3::Vector3(const float* p_Xyz) 	
{ 
	m_X = p_Xyz[0];
	m_Y = p_Xyz[1];
	m_Z = p_Xyz[2];
}

inline Vector3::Vector3(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
}

// + operator (vector sign)
inline Vector3 Vector3::operator+() const
{
	return (*this);
}

// - operator (vector sign)
inline Vector3 Vector3::operator-() const
{
	return Vector3(-m_X, -m_Y, -m_Z);
}

// += operator
inline Vector3& Vector3::operator+=(const Vector3& p_Vec)
{
	m_X += p_Vec.m_X;
	m_Y += p_Vec.m_Y;
	m_Z += p_Vec.m_Z;

	return (*this);
}

// -= operator
inline Vector3& Vector3::operator-=(const Vector3& p_Vec)
{
	m_X -= p_Vec.m_X;
	m_Y -= p_Vec.m_Y;
	m_Z -= p_Vec.m_Z;

	return (*this);
}

// dot product
inline float Vector3::operator*=(const Vector3& p_Vec)
{
	return ((m_X * p_Vec.m_X) + (m_Y * p_Vec.m_Y) + (m_Z * p_Vec.m_Z));
}

// scalar multiplication
inline Vector3& Vector3::operator*=(const float p_Value)
{
	m_X *= p_Value;
	m_Y *= p_Value;
	m_Z *= p_Value;

	return (*this);
}

// /= operator
inline Vector3& Vector3::operator/=(const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	m_X *= invValue;
	m_Y *= invValue;
	m_Z *= invValue;

	return (*this);
}

// assignment operator
inline Vector3& Vector3::operator=(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;

	return (*this);
}

// friend function for addition, substraction and dot product
inline const Vector3 operator+(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	return Vector3(p_Vec1.m_X + p_Vec2.m_X, p_Vec1.m_Y + p_Vec2.m_Y, p_Vec1.m_Z + p_Vec2.m_Z);
}

inline const Vector3 operator-(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	return Vector3(p_Vec1.m_X - p_Vec2.m_X, p_Vec1.m_Y - p_Vec2.m_Y, p_Vec1.m_Z - p_Vec2.m_Z);
}

inline float operator*(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	return ((p_Vec1.m_X * p_Vec2.m_X) + (p_Vec1.m_Y * p_Vec2.m_Y) + (p_Vec1.m_Z * p_Vec2.m_Z));
}

// vector/scalar multiplication
inline const Vector3 operator*(const Vector3& p_Vec, const float p_Value)
{
	return Vector3(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value, p_Vec.m_Z * p_Value);
}

inline const Vector3 operator*(const float p_Value, const Vector3& p_Vec)
{
	return Vector3(p_Vec.m_X * p_Value, p_Vec.m_Y * p_Value, p_Vec.m_Z * p_Value);
}

// vector/scalar division
inline const Vector3 operator/(const Vector3& p_Vec, const float p_Value)
{
	const float invValue = 1.0f / p_Value;
	return Vector3(p_Vec.m_X * invValue, p_Vec.m_Y * invValue, p_Vec.m_Z * invValue);
}

inline const Vector3 operator/(const float p_Value, const Vector3& p_Vec)
{
	const float invValue = 1.0f / p_Value;
	return Vector3(p_Vec.m_X * invValue, p_Vec.m_Y * invValue, p_Vec.m_Z * invValue);
}

// comparison operators
inline bool operator==(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	if( Abs(p_Vec1.m_X - p_Vec2.m_X) < EPSILON && Abs(p_Vec1.m_Y - p_Vec2.m_Y) < EPSILON && 
		Abs(p_Vec1.m_Z - p_Vec2.m_Z) < EPSILON)
		return true;

	return false;
}

inline bool operator<(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	if(p_Vec1.LengthSqr() < p_Vec2.LengthSqr())
		return true;

	return false;
}

// set vector from a float pointer
inline void Vector3::Set(const float* p_Xyz)
{
	m_X = p_Xyz[0];
	m_Y = p_Xyz[1];
	m_Z = p_Xyz[2];
}

// copy vector from another vector
inline void Vector3::Set(const Vector3& p_Vec)
{
	m_X = p_Vec.m_X;
	m_Y = p_Vec.m_Y;
	m_Z = p_Vec.m_Z;
}

// set vector components
inline void Vector3::Set(const float p_X, const float p_Y, const float p_Z)
{
	m_X = p_X;
	m_Y = p_Y;
	m_Z = p_Z;
}

// zeros all vector components
inline void Vector3::Zero()
{
	m_X = m_Y = m_Z = 0.0f;
}

// length function
inline float Vector3::Length() const
{
	return Sqrt(Sqr(m_X) + Sqr(m_Y) + Sqr(m_Z));
}

// returns the square of the length (used for == comparison)
inline float Vector3::LengthSqr() const
{
	return Sqr(m_X) + Sqr(m_Y) + Sqr(m_Z);
}

// normalizes the vector (make a unit vector)
inline void Vector3::Normalize()
{
	const float length = Length();
	if(length > Math::EPSILON)
	{
		const float invLength = 1.0f / length;
		m_X *= invLength;
		m_Y *= invLength;
		m_Z *= invLength;
	}
}

// normalizes the vector (make a unit vector) using the fast reciprocal sqrt function
inline void Vector3::NormalizeFast()
{
	const float invLength = InvSqrt(LengthSqr());
	m_X *= invLength;
	m_Y *= invLength;
	m_Z *= invLength;
}

// cross product with itself
inline Vector3& Vector3::Cross(const Vector3& p_Vec)
{
	m_X = (m_Y * p_Vec.m_Z) - (m_Z * p_Vec.m_Y);
	m_Y = (m_Z * p_Vec.m_X) - (m_X * p_Vec.m_Z);
	m_Z = (m_X * p_Vec.m_Y) - (m_Y * p_Vec.m_X);

	return (*this);
}

// friend function for cross product
inline const Vector3 Cross(const Vector3& p_Vec1, const Vector3& p_Vec2)
{
	return Vector3( (p_Vec1.m_Y * p_Vec2.m_Z) - (p_Vec1.m_Z * p_Vec2.m_Y),
					(p_Vec1.m_Z * p_Vec2.m_X) - (p_Vec1.m_X * p_Vec2.m_Z),
					(p_Vec1.m_X * p_Vec2.m_Y) - (p_Vec1.m_Y * p_Vec2.m_X));
}

// interpolate the two vectors and store the result in the current intance
inline void Vector3::Interpolate(const Vector3& p_Vec1, const Vector3& p_Vec2, const float p_Value)
{
	const float oneMinusValue = 1.0f - p_Value;

	m_X = (p_Vec1.m_X * oneMinusValue) + (p_Vec2.m_X * p_Value);
	m_Y = (p_Vec1.m_Y * oneMinusValue) + (p_Vec2.m_Y * p_Value);
	m_Z = (p_Vec1.m_Z * oneMinusValue) + (p_Vec2.m_Z * p_Value);
}

} // namespace Math

#endif // VECTOR3_H
